/* * Copyright Technophobia Ltd 2012 * * This file is part of Substeps. * * Substeps is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Substeps is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Substeps. If not, see <http://www.gnu.org/licenses/>. */ package com.technophobia.substeps.report; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; import org.apache.commons.lang3.StringEscapeUtils; import org.joda.time.Duration; import org.joda.time.format.PeriodFormat; import org.joda.time.format.PeriodFormatter; import com.google.common.io.Files; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.technophobia.substeps.execution.AbstractExecutionNodeVisitor; import com.technophobia.substeps.execution.ExecutionNodeResult; import com.technophobia.substeps.execution.node.ExecutionNode; import com.technophobia.substeps.execution.node.IExecutionNode; import com.technophobia.substeps.execution.node.NodeWithChildren; import com.technophobia.substeps.execution.node.StepImplementationNode; import com.technophobia.substeps.model.exception.SubstepsRuntimeException; public final class DetailedJsonBuilder extends AbstractExecutionNodeVisitor<JsonObject> { private final ReportData reportData; private final String screenshotFolder; private BufferedWriter writer; public static void writeDetailJson(ReportData reportData, String screenshotFolder, File jsonFile) { new DetailedJsonBuilder(reportData, screenshotFolder).writeFile(jsonFile); } private DetailedJsonBuilder(ReportData reportData, String screenshotFolder) { this.reportData = reportData; this.screenshotFolder = screenshotFolder; } private void writeFile(File jsonFile) { try { writer = Files.newWriter(jsonFile, Charset.defaultCharset()); writer.append("var detail = new Array();"); for (ExecutionNode rootNode : reportData.getRootNodes()) { for (JsonObject nodeAsJson : rootNode.accept(this)) { writer.append("\ndetail[" + nodeAsJson.get("id") + "]=" + nodeAsJson.toString() + ";"); } } } catch (IOException e) { throw new SubstepsRuntimeException("Failed writing to detail json file"); } finally { if (writer != null) { try { writer.flush(); writer.close(); } catch (IOException e) { throw new SubstepsRuntimeException("Failed writing to detail json file"); } } } } @Override public JsonObject visit(NodeWithChildren<?> node) { return createBasicDetailsWithChildDetails(node.getClass().getSimpleName().toString(), node, node.getChildren()); } @Override public JsonObject visit(StepImplementationNode stepImplementationNode) { JsonObject json = createBasicDetails("Step", stepImplementationNode); addLinkToScreenshot(stepImplementationNode.getResult(), json); String methodInfo = createMethodInfo(stepImplementationNode); json.addProperty("method", methodInfo); return json; } private JsonObject createBasicDetailsWithChildDetails(String nodeType, IExecutionNode node, List<? extends IExecutionNode> childNodes) { JsonObject json = createBasicDetails(nodeType, node); addDetailsForChildren(json, childNodes); return json; } public JsonObject createBasicDetails(String nodeType, IExecutionNode node) { JsonObject thisNode = new JsonObject(); thisNode.addProperty("nodetype", nodeType); thisNode.addProperty("filename", node.getFilename()); thisNode.addProperty("result", node.getResult().getResult().toString()); thisNode.addProperty("id", node.getId()); thisNode.addProperty("emessage", getExceptionMessage(node)); thisNode.addProperty("stacktrace", getStackTrace(node)); thisNode.addProperty("runningDurationMillis", node.getResult().getRunningDuration()); thisNode.addProperty("runningDurationString", convert(node.getResult().getRunningDuration())); String description = node.getDescription() == null ? null : node.getDescription().trim(); String descriptionEscaped = replaceNewLines(StringEscapeUtils.escapeHtml4(description)); thisNode.addProperty("description", descriptionEscaped); return thisNode; } private void addLinkToScreenshot(ExecutionNodeResult result, JsonObject thisNode) { if (result.getScreenshot() != null) { thisNode.addProperty("screenshot", screenshotFolder + File.separator + result.getExecutionNodeId() + ScreenshotWriter.SCREENSHOT_SUFFIX); } } private String convert(Long runningDurationMillis) { return runningDurationMillis == null ? "No duration recorded" : convert(runningDurationMillis.longValue()); } private String convert(long runningDurationMillis) { Duration duration = new Duration(runningDurationMillis); PeriodFormatter formatter = PeriodFormat.getDefault(); return formatter.print(duration.toPeriod()); } private void addDetailsForChildren(JsonObject json, List<? extends IExecutionNode> childNodes) { JsonArray children = new JsonArray(); json.add("children", children); for (IExecutionNode childNode : childNodes) { JsonObject childObject = new JsonObject(); childObject.addProperty("result", childNode.getResult().getResult().toString()); childObject.addProperty("description", StringEscapeUtils.escapeHtml4(childNode.getDescription())); children.add(childObject); } } private String createMethodInfo(StepImplementationNode node) { final StringBuilder methodInfoBuffer = new StringBuilder(); node.appendMethodInfo(methodInfoBuffer); String methodInfo = methodInfoBuffer.toString(); if (methodInfo.contains("\"")) { methodInfo = methodInfo.replace("\"", "\\\""); } return replaceNewLines(methodInfo); } private String getExceptionMessage(IExecutionNode node) { String exceptionMessage = ""; if (node.getResult().getThrown() != null) { final String exceptionMsg = StringEscapeUtils.escapeHtml4(node.getResult().getThrown().getMessage()); exceptionMessage = replaceNewLines(exceptionMsg); } return exceptionMessage; } private String getStackTrace(IExecutionNode node) { String stackTrace = ""; if (node.getResult().getThrown() != null) { final StackTraceElement[] stackTraceElements = node.getResult().getThrown().getStackTrace(); final StringBuilder buf = new StringBuilder(); for (final StackTraceElement e : stackTraceElements) { buf.append(StringEscapeUtils.escapeHtml4(e.toString().trim())).append("<br/>"); } stackTrace = buf.toString(); } return stackTrace; } private String replaceNewLines(final String s) { if (s != null && s.contains("\n")) { return s.replaceAll("\n", "<br/>"); } else { return s; } } }